import os
import wx
import threading
import yaml
from core.core_extra import *
from core.adb_interface import AdbInterface
from core import thread_extra
from core.wx_logger import *
from core.yaml_extra import YamlLoaderWithLog, YamlDumperWithLog
from logic.custom_logic import *
from widgets.custom_script_dialog import DlgCustomScript


class PnlCustom(wx.Panel):
    """自定义脚本子窗口"""

    MsgType = CLogger.MsgType

    def __init__(self, parent=None):
        wx.Panel.__init__(self, parent)
        # control
        self.lbl_dev = wx.StaticText(self, label="设备列表：")
        self.cmb_dev = wx.ComboBox(self, style=wx.CB_READONLY | wx.TEXT_ALIGNMENT_CENTER)

        self.lbl_script = wx.StaticText(self, label="选择脚本：")
        self.btn_script_add = wx.Button(self, label="+", size=(25, 25))
        self.btn_script_del = wx.Button(self, label="-", size=(25, 25))
        self.btn_script_mod = wx.Button(self, label="M", size=(25, 25))
        self.btn_script_new = wx.Button(self, label="N", size=(25, 25))
        self.btn_script_edt = wx.Button(self, label="E", size=(25, 25))
        self.cmb_script = wx.ComboBox(self, style=wx.CB_READONLY | wx.TEXT_ALIGNMENT_CENTER)

        self.lbl_progress = wx.StaticText(self, label="整体进度：")
        if version_not_less(wx.VERSION, "4.1.0"):
            self.pgs_progress = wx.Gauge(self, range=100, style=wx.GA_SMOOTH | wx.GA_HORIZONTAL | wx.GA_TEXT)
            self.lbl_pgs_val = None
        else:
            self.pgs_progress = wx.Gauge(self, range=100, style=wx.GA_SMOOTH | wx.GA_HORIZONTAL)
            self.lbl_pgs_val = wx.StaticText(self, label="--.-%")
        self.btn_refresh = wx.Button(self, label="刷新")
        self.btn_start = wx.Button(self, label="开始")
        self.btn_pause = wx.Button(self, label="暂停")
        self.btn_stop = wx.Button(self, label="停止")
        #  box
        self.box_dev = wx.BoxSizer()
        self.box_script_label = wx.BoxSizer()
        self.box_scrtpt_buttons = wx.BoxSizer()
        self.box_scrtpt_buttons2 = wx.BoxSizer(wx.VERTICAL)
        self.box_script_select = wx.BoxSizer()
        self.box_pgsctrl = wx.BoxSizer()
        self.box_op = wx.BoxSizer()
        self.box_all = wx.BoxSizer(wx.VERTICAL)
        self.box_border_h = wx.BoxSizer()
        self.box_border_v = wx.BoxSizer(wx.VERTICAL)
        # var
        self.txt_log: wx.TextCtrl = None
        self.log_ctrl = None
        self.progress_ctrl = ProgressBar(self.pgs_progress, self.lbl_pgs_val)
        self.run_thread = None
        self.adb = AdbInterface()
        self.devices = []
        self.data_dir = os.curdir + "/custom.data/"
        self.device_conf_array = []
        self.conf = {}
        self.init_ui()
        threading.Thread(target=lambda: (self.load_conf(), self.init_data())).start()

    def init_ui(self):
        self.cmb_dev.Disable()
        self.cmb_dev.Bind(wx.EVT_COMBOBOX, self.cmb_dev_selected)
        self.btn_script_add.Bind(wx.EVT_BUTTON, self.btn_script_add_clicked)
        self.btn_script_add.SetToolTip("添加已存在的脚本文件到当前设备下")
        self.btn_script_del.Bind(wx.EVT_BUTTON, self.btn_script_del_clicked)
        self.btn_script_del.SetToolTip("删除当前脚本项，不删除文件")
        self.btn_script_mod.Bind(wx.EVT_BUTTON, self.btn_script_mod_clicked)
        self.btn_script_mod.SetToolTip("修改当前脚本项名称，不修改文件")
        self.btn_script_new.Bind(wx.EVT_BUTTON, self.btn_script_new_clicked)
        self.btn_script_new.SetToolTip("新建脚本到当前设备")
        self.btn_script_edt.Bind(wx.EVT_BUTTON, self.btn_script_edt_clicked)
        self.btn_script_edt.SetToolTip("编辑当前脚本内容")
        self.btn_refresh.Bind(wx.EVT_BUTTON, self.btn_refresh_clicked)
        self.btn_start.Bind(wx.EVT_BUTTON, self.btn_start_clicked)
        self.btn_pause.Bind(wx.EVT_BUTTON, self.btn_pause_clicked)
        self.btn_pause.Disable()
        self.btn_stop.Bind(wx.EVT_BUTTON, self.btn_stop_clicked)
        self.btn_stop.Disable()

        self.box_dev.AddMany([(self.lbl_dev, 0, wx.ALIGN_CENTER_VERTICAL),
                              (self.cmb_dev, 1, wx.ALIGN_CENTER_VERTICAL)])

        self.box_scrtpt_buttons.AddMany([(self.btn_script_add, 0, wx.ALIGN_CENTER_VERTICAL),
                                         (self.btn_script_del, 0, wx.ALIGN_CENTER_VERTICAL),
                                         (self.btn_script_mod, 0, wx.ALIGN_CENTER_VERTICAL),
                                         (self.btn_script_new, 0, wx.ALIGN_CENTER_VERTICAL),
                                         (self.btn_script_edt, 0, wx.ALIGN_CENTER_VERTICAL)])
        self.box_scrtpt_buttons2.Add(self.box_scrtpt_buttons, flag=wx.ALIGN_RIGHT)
        self.box_script_label.AddMany([(self.lbl_script, 0, wx.ALIGN_BOTTOM),
                                       (self.box_scrtpt_buttons2, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)])
        self.box_script_select.Add(self.cmb_script, proportion=1, flag=wx.EXPAND)

        self.box_pgsctrl.AddMany([(self.lbl_progress, 0, wx.ALIGN_CENTER_VERTICAL),
                                  (self.pgs_progress, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)])
        if version_less(wx.VERSION, "4.1.0"):
            self.box_pgsctrl.Add(self.lbl_pgs_val, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, border=5)
        self.box_op.AddMany([(self.btn_refresh, 1, wx.ALIGN_RIGHT),
                             (self.btn_start, 1, wx.ALIGN_RIGHT | wx.LEFT, 5),
                             (self.btn_pause, 1, wx.ALIGN_RIGHT | wx.LEFT, 5),
                             (self.btn_stop, 1, wx.ALIGN_RIGHT | wx.LEFT, 5)])

        self.box_all.AddMany([(self.box_dev, 0, wx.EXPAND | wx.TOP, 5),
                              (self.box_script_label, 0, wx.EXPAND | wx.TOP, 15),
                              (self.box_script_select, 0, wx.EXPAND | wx.TOP, 5),
                              (self.box_pgsctrl, 0, wx.EXPAND | wx.TOP, 15),
                              (self.box_op, 0, wx.EXPAND | wx.TOP, 5)])
        self.box_border_h.Add(self.box_all, proportion=1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=15)
        self.box_border_v.Add(self.box_border_h, proportion=1, flag=wx.EXPAND | wx.TOP | wx.BOTTOM, border=10)

        self.SetSizer(self.box_border_v)
        self.Bind(wx.EVT_WINDOW_DESTROY, self.on_closing)
        return True

    def init_data(self):
        self.cmb_dev.Disable()
        self.cmb_script.Disable()
        # devices
        if self.cmb_dev.GetSelection() is not wx.NOT_FOUND:
            self.conf["last_device"] = self.cmb_dev.GetSelection()
        self.log_msg("搜索设备中....")
        _, dev = self.adb.list_devices_detail()
        self.log_msg("OK\n")
        self.devices.clear()
        self.cmb_dev.Clear()
        self.cmb_script.Clear()
        self.device_conf_array.clear()
        for d in dev:
            d_sn = d["SN"] if "SN" in d else "null"
            d_model = d["model"] if "model" in d else "null"
            d_state = d["state"] if "state" in d else "null"
            d_product = d["product"] if "product" in d else "null"
            d_device = d["device"] if "device" in d else "null"
            self.cmb_dev.Append(
                d_sn + " (" + d_model + ") - " + ("online" if d_state == "device" else "offline"))
            self.devices.append([d_sn, d_sn, d_model, d_product, d_device])
        if not self.cmb_dev.IsEmpty():
            self.conf["last_device"] = max(min(self.conf["last_device"], self.cmb_dev.GetCount()-1), 0)
            self.cmb_dev.Select(self.conf["last_device"])
            self.cmb_dev_selected(self.cmb_dev.GetSelection())
        self.cmb_dev.Enable()
        self.cmb_script.Enable()
        return True

    def set_log_ctrl(self, ctrl: wx.TextCtrl):
        self.txt_log = ctrl
        self.log_ctrl = TxtCtrlLog(ctrl)
        pass

    def on_closing(self, e):
        self.btn_stop_clicked(wx.CommandEvent())
        self.save_conf()

    def btn_refresh_clicked(self, e: wx.CommandEvent):
        self.init_data()
        e.Skip()
        pass

    def btn_start_clicked(self, e: wx.CommandEvent):
        if self.run_thread is not None and self.run_thread.is_alive():
            wx.MessageBox("已经启动脚本！", "启动", style=wx.ICON_INFORMATION)
            return
        # check data
        if self.cmb_dev.GetSelection() is wx.NOT_FOUND:
            if self.cmb_dev.GetCount() is 1:
                self.cmb_dev.SetSelection(0)
            else:
                wx.MessageBox("请选择Android设备！", "启动", style=wx.ICON_ERROR)
                return
        dev_sel = self.cmb_dev.GetSelection()
        scp_sel = self.cmb_script.GetSelection()
        try:
            file_name = self.data_dir + regularize_filename(self.devices[dev_sel][0]) + "/" + \
                        self.device_conf_array[dev_sel]["scripts"][scp_sel]["file"]
            self.log_msg("==================================================\n")
            self.log_msg("检测设备......    ")
            self.adb.set_device(self.devices[dev_sel][0])
            succ = self.adb.check_device()
            if succ:
                self.log_msg("就绪.\n")
            else:
                self.log_msg("失败.\n", self.MsgType.ERROR)
                return
            conf = {}
            if not os.path.exists(file_name):
                self.log_msg("脚本文件不存在！\n", self.MsgType.ERROR)
                if wx.YES == wx.MessageBox("所选脚本文件丢失，是否删除此项？", "启动", style=wx.YES_NO):
                    self.btn_script_del_clicked(e)
                return False
            with YamlLoaderWithLog(file=file_name, logger=self.log_ctrl) as y:
                conf = y.load_dict()
            if len(conf) is 0:
                self.log_msg("脚本为空！\n", self.MsgType.ERROR)
            actions = []
            tip = None
            name = "None"
            times = 1
            if "loop" in conf:
                times = -1 if bool(conf["loop"]) else 1
            if "times" in conf:
                times = conf["times"]
            if "name" in conf:
                name = conf["name"]
            if "tip" in conf:
                tip = conf["tip"]
            else:
                tip = "执行脚本：" + name
            if "actions" in conf:
                actions = conf["actions"]
            self.log_msg("脚本名称：{}\n".format(name))
            self.log_msg("循环次数：{}\n".format(times if times > 0 else "无限"))
            self.log_msg("提示：{}\n".format(tip), self.MsgType.INFO)
            tip_dlg = wx.MessageDialog(self, tip, "提示", style=wx.OK|wx.CANCEL)
            tip_dlg.SetOKCancelLabels("开始执行", "我再想想")
            if wx.ID_CANCEL ==  tip_dlg.ShowModal():
                self.log_msg("用户取消！\n", self.MsgType.INFO)
                return True
            if len(actions) is 0:
                self.log_msg("没有操作记录！\n", self.MsgType.ERROR)
            # run
            self.run_thread = CustomScriptThread(self.adb, actions, times=times, file=file_name)
            self.run_thread.set_logger_ctrl(self.log_ctrl)
            self.run_thread.set_progress_ctrl(self.progress_ctrl)
            self.run_thread.add_callback_thread_started(PnlCustom.deactivate_btn_start, self)
            self.run_thread.add_callback_thread_over(PnlCustom.activate_btn_start, self)
            self.log_msg("开始执行...\n")
            self.run_thread.start()
        except Exception as ex:
            self.log_msg(ex, self.MsgType.ERROR)
        e.Skip()
        pass

    def btn_pause_clicked(self, e):
        if self.run_thread.is_alive() and not self.run_thread.is_paused():
            self.btn_pause.SetLabelText("继续")
            self.run_thread.require_pause()
        else:
            self.btn_pause.SetLabelText("暂停")
            self.run_thread.require_continue()
        pass

    def btn_stop_clicked(self, e: wx.CommandEvent):
        if self.run_thread is not None:
            if self.run_thread.is_alive():
                self.log_msg("\n正在停止...\n")
                self.run_thread.require_stop()
        e.Skip()

    def cmb_dev_selected(self, e: wx.CommandEvent):
        if isinstance(e, int):
            dev_sel = e
        elif isinstance(e, wx.CommandEvent):
            dev_sel = e.GetSelection()
        else:
            return False
        if self.cmb_script.GetSelection() is not wx.NOT_FOUND:
            self.conf["last_script"] = self.cmb_script.GetSelection()
        self.cmb_script.Clear()
        if dev_sel is wx.NOT_FOUND:
            return False
        if len(self.devices) is 0:
            return False
        if len(self.device_conf_array) is 0 or \
                len(self.device_conf_array[dev_sel]) is 0:
            self.load_device_conf(dev_sel)
        if len(self.device_conf_array) is 0 or \
                len(self.device_conf_array[dev_sel]) is 0:
            return False
        conf: dict = self.device_conf_array[dev_sel]
        if "scripts" in conf:
            self.cmb_script.AppendItems([c["name"] + " - " + c["file"] for c in conf["scripts"]])
        else:
            conf["scripts"] = []
        if not self.cmb_script.IsEmpty():
            self.conf["last_script"] = min(max(0, self.conf["last_script"]), self.cmb_script.GetCount()-1)
            self.cmb_script.Select(self.conf["last_script"])

    def btn_script_add_clicked(self, e: wx.CommandEvent):
        dev_sel = self.cmb_dev.GetSelection()
        if dev_sel is wx.NOT_FOUND:
            self.cmb_script.Clear()
            return False
        if len(self.devices) is 0:
            self.cmb_script.Clear()
            return False
        if len(self.device_conf_array) is 0:
            self.load_device_conf(dev_sel)
        if len(self.device_conf_array) is 0:
            self.cmb_script.Clear()
            return False
        if "scripts" not in self.device_conf_array[dev_sel]:
            self.device_conf_array[dev_sel]["scripts"] = []
        dlg = DlgAddScript(self, self.data_dir + regularize_filename(self.devices[dev_sel][0]))
        if dlg.ShowModal() == wx.ID_OK:
            self.device_conf_array[dev_sel]["scripts"].append(dict(dlg.script))
            self.cmb_script.Append(dlg.script["name"] + " - " + dlg.script["file"])
            self.save_device_conf(dev_sel)
        dlg.Destroy()
        if not self.cmb_script.IsEmpty() and self.cmb_script.GetSelection() is wx.NOT_FOUND:
            self.cmb_script.Select(0)
        pass

    def btn_script_del_clicked(self, e: wx.CommandEvent):
        dev_sel = self.cmb_dev.GetSelection()
        scp_sel = self.cmb_script.GetSelection()
        if dev_sel is wx.NOT_FOUND:
            return
        if scp_sel is wx.NOT_FOUND:
            return
        if len(self.device_conf_array) is 0:
            return
        conf: dict = self.device_conf_array[dev_sel]
        if "scripts" in conf:
            deleted = conf["scripts"].pop(scp_sel)
            self.cmb_script.Delete(scp_sel)
        self.save_device_conf(dev_sel)
        self.log_msg("脚本记录已删除，未处理脚本文件。")
        if not self.cmb_script.IsEmpty() and self.cmb_script.GetSelection() is wx.NOT_FOUND:
            self.cmb_script.Select(0)
        pass

    def btn_script_mod_clicked(self, e: wx.CommandEvent):
        dev_sel = self.cmb_dev.GetSelection()
        scp_sel = self.cmb_script.GetSelection()
        if dev_sel is wx.NOT_FOUND:
            return False
        if len(self.devices) is 0:
            return False
        if len(self.device_conf_array) is 0:
            self.load_device_conf(dev_sel)
        if len(self.device_conf_array) is 0:
            return False
        if scp_sel is wx.NOT_FOUND:
            return False
        if "scripts" not in self.device_conf_array[dev_sel]:
            self.device_conf_array[dev_sel]["scripts"] = []
        scp = self.device_conf_array[dev_sel]["scripts"][scp_sel]
        dlg = DlgAddScript(self, self.data_dir + regularize_filename(self.devices[dev_sel][0]), scp["name"], scp["file"])
        if dlg.ShowModal() == wx.ID_OK:
            self.device_conf_array[dev_sel]["scripts"][scp_sel] = dict(dlg.script)
            self.cmb_script.Clear()
            self.cmb_script.AppendItems(
                [s["name"] + " - " + s["file"] for s in self.device_conf_array[dev_sel]["scripts"]])
            self.save_device_conf(dev_sel)
        dlg.Destroy()
        if not self.cmb_script.IsEmpty() and self.cmb_script.GetSelection() is wx.NOT_FOUND:
            self.cmb_script.Select(0)
        pass

    def btn_script_new_clicked(self, e: wx.CommandEvent):
        dev_sel = self.cmb_dev.GetSelection()
        if dev_sel is wx.NOT_FOUND:
            return False
        if len(self.devices) is 0:
            return False
        if len(self.device_conf_array) is 0:
            self.load_device_conf(dev_sel)
        if len(self.device_conf_array) is 0:
            return False
        if "scripts" not in self.device_conf_array[dev_sel]:
            self.device_conf_array[dev_sel]["scripts"] = []
        dlg = DlgCustomScript(self, self.data_dir + regularize_filename(self.devices[dev_sel][0]), dev_sn=self.devices[dev_sel])
        if dlg.ShowModal() == wx.ID_OK:
            scp = {"name": dlg.script["name"], "file": dlg.script_file}
            self.device_conf_array[dev_sel]["scripts"].append(scp)
            self.cmb_script.AppendItems(scp["name"] + " - " + scp["file"])
            self.save_device_conf(dev_sel)
        dlg.Destroy()
        if not self.cmb_script.IsEmpty() and self.cmb_script.GetSelection() is wx.NOT_FOUND:
            self.cmb_script.Select(0)
        pass

    def btn_script_edt_clicked(self, e: wx.CommandEvent):
        dev_sel = self.cmb_dev.GetSelection()
        scp_sel = self.cmb_script.GetSelection()
        if dev_sel is wx.NOT_FOUND:
            return False
        if len(self.devices) is 0:
            return False
        if len(self.device_conf_array) is 0:
            self.load_device_conf(dev_sel)
        if len(self.device_conf_array) is 0:
            return False
        if scp_sel is wx.NOT_FOUND:
            return False
        if "scripts" not in self.device_conf_array[dev_sel]:
            self.device_conf_array[dev_sel]["scripts"] = []
        scp = self.device_conf_array[dev_sel]["scripts"][scp_sel]
        dlg = DlgCustomScript(self, self.data_dir + regularize_filename(self.devices[dev_sel][0]), scp["file"], dev_sn=self.devices[dev_sel])
        if dlg.ShowModal() == wx.ID_OK:
            pass
        dlg.Destroy()
        if not self.cmb_script.IsEmpty() and self.cmb_script.GetSelection() is wx.NOT_FOUND:
            self.cmb_script.Select(0)
        pass

    def load_conf(self):
        try:
            self.conf["last_device"] = -1
            self.conf["last_script"] = -1
            with YamlLoaderWithLog(file="custom.data/custom.conf", logger=self.log_ctrl) as y:
                new_conf = y.load_dict()
                if isinstance(new_conf, dict):
                    self.conf.update(new_conf)
            self.cmb_dev.SetSelection(self.conf["last_device"])
            self.cmb_script.SetSelection(self.conf["last_script"])
            return True
        except Exception as ex:
            print("加载配置失败：", ex)
            return False

    def save_conf(self):
        try:
            self.conf["last_device"] = self.cmb_dev.GetSelection()
            self.conf["last_script"] = self.cmb_script.GetSelection()
            YamlDumperWithLog("custom.data/custom.conf", logger=self.log_ctrl).safe_dump(self.conf)
            return True
        except Exception as ex:
            print("存储配置失败：", ex)
            return False

    def load_device_conf(self, dev):
        if len(self.devices) is 0:
            return False
        if dev < 0 or dev >= len(self.devices):
            return False
        if len(self.device_conf_array) != len(self.devices):
            self.device_conf_array = [{}] * len(self.devices)

        self.device_conf_array[dev].clear()
        # find available path
        file_dir = os.path.join(self.data_dir, regularize_filename(self.devices[dev][0]))
        for i in range(1, len(self.devices[dev])):
            if os.path.exists(file_dir):
                break
            file_dir = os.path.join(self.data_dir, regularize_filename(self.devices[dev][i]))
            self.devices[dev][0] = self.devices[dev][i]
        # create directory if path is not found
        if not os.path.exists(file_dir):
            return False
        file_name = os.path.join(file_dir, "script.conf")
        try:
            if not os.path.exists(file_name):
                if not os.path.exists(file_dir):
                    os.mkdir(file_dir)
                else:
                    # os.mknod(file_name) # windows上不支持
                    with open(file_name, "w+") as f:
                        pass
                return True
            with YamlLoaderWithLog(file=file_name, logger=self.log_ctrl, raise_again=True) as f:
                conf = f.load_dict()
                self.device_conf_array[dev] = conf
            return True
        except Exception as ex:
            self.log_msg("Load configure for device '{}': {}".format(self.devices[dev][0], ex))
            return False

    def save_device_conf(self, dev):
        if len(self.devices) is 0:
            return False
        if dev < 0 or dev >= len(self.devices):
            return False
        if len(self.device_conf_array) != len(self.devices):
            return False
        if "scripts" not in self.device_conf_array[dev]:
            self.device_conf_array[dev]["scripts"] = []
        if "count" not in self.device_conf_array[dev]:
            self.device_conf_array[dev]["count"] = len(self.device_conf_array[dev]["scripts"])
        # find available path
        file_dir = os.path.join(self.data_dir, regularize_filename(self.devices[dev][0]))
        for i in range(1, len(self.devices[dev])):
            if os.path.exists(file_dir):
                break
            file_dir = os.path.join(self.data_dir, regularize_filename(self.devices[dev][i]))
            self.devices[dev][0] = self.devices[dev][i]
        # create directory if path is not found
        if not os.path.exists(file_dir):
            self.devices[dev][0] = self.devices[dev][2]
            file_dir = os.path.join(self.data_dir, regularize_filename(self.devices[dev][0]))
            os.mkdir(file_dir)
        file_name = os.path.join(file_dir, "script.conf")
        try:
            with YamlDumperWithLog(file_name, logger=self.log_ctrl, raise_again=True) as f:
                f.dump(self.device_conf_array[dev])
            return True
        except Exception as ex:
            self.log_msg("Save configure for device '{}': {}".format(self.devices[dev][0], ex))
            return False

    def log_msg(self, msg, type: MsgType = MsgType.DEFAULT):
        if self.log_ctrl is None:
            return
        self.log_ctrl.write(msg, type)

    def activate_btn_start(self):
        self.btn_start.Enable()
        self.btn_pause.Disable()
        self.btn_stop.Disable()
        self.btn_pause.SetLabelText("继续")

    def deactivate_btn_start(self):
        self.btn_start.Disable()
        self.btn_pause.Enable()
        self.btn_stop.Enable()
        self.btn_pause.SetLabelText("暂停")


class DlgAddScript(wx.Dialog):
    def __init__(self, parent, dir, script_name=None, script_file=None):
        wx.Dialog.__init__(self, parent=parent, title="添加脚本")
        self.file_dir = dir
        self.script = {"name": script_name, "file": script_file}
        self.lbl_name = wx.StaticText(self, label="脚本名称：")
        self.txt_name = wx.TextCtrl(self)
        self.lbl_file = wx.StaticText(self, label="脚本文件：")
        self.txt_file = wx.TextCtrl(self)
        self.btn_file = wx.Button(self, label="选择...")
        self.btn_ok = wx.Button(self, id=wx.ID_OK, label="确定")
        self.btn_cancel = wx.Button(self, id=wx.ID_CANCEL, label="取消")

        self.box_all = wx.BoxSizer(wx.VERTICAL)
        self.box_name = wx.BoxSizer()
        self.box_name.AddMany([(self.lbl_name, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10),
                               (self.txt_name, 1, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)])
        self.box_file = wx.BoxSizer()
        self.box_file.AddMany([(self.lbl_file, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10),
                               (self.txt_file, 1, wx.ALIGN_CENTER_VERTICAL),
                               (self.btn_file, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)])
        self.box_button = wx.BoxSizer()
        self.box_button.AddMany([(self.btn_ok, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10),
                                 (self.btn_cancel, 1, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)])
        self.box_all.AddMany([(self.box_name, 1, wx.EXPAND | wx.TOP, 5),
                              (self.box_file, 1, wx.EXPAND | wx.TOP, 5),
                              (self.box_button, 1, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)])
        self.SetSizer(self.box_all)
        self.SetSize(400, 150)
        self.btn_file.Bind(wx.EVT_BUTTON, self.btn_file_clicked)
        self.btn_ok.Bind(wx.EVT_BUTTON, self.on_ok)
        # self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_cancel)
        if script_file is not None:
            self.txt_file.SetValue(script_file)
        if script_name is not None:
            self.txt_name.SetValue(script_name)

    def btn_file_clicked(self, e):
        fd = wx.FileDialog(self, message="选择脚本文件", defaultDir=self.file_dir,
                           wildcard="脚本文件|*.yml", style=wx.FD_FILE_MUST_EXIST | wx.FD_OPEN)
        fd.SetPath(self.file_dir + "/")
        if fd.ShowModal() == wx.ID_OK:
            p1 = os.path.abspath(self.file_dir)
            p2 = os.path.abspath(os.path.dirname(fd.GetPath()))
            if p1 not in p2:
                wx.MessageBox("不允许切换到上级目录，只可以选择本设备下的脚本！", "脚本目录错误", parent=self)
            else:
                self.txt_file.SetValue(fd.GetFilename())
                if self.txt_name.IsEmpty():
                    try:
                        with YamlLoaderWithLog(fd.GetPath(), raise_again=True) as y:
                            conf = y.load_dict()
                            if "name" in conf:
                                self.txt_name.SetValue(str(conf["name"]))
                    except Exception as ex:
                        wx.MessageBox(str(ex), "读取脚本名")
        pass

    def on_ok(self, e):
        if self.txt_name.IsEmpty():
            wx.MessageBox("脚本名称不能为空", "添加脚本", parent=self)
            return
        if self.txt_file.IsEmpty():
            wx.MessageBox("脚本文件不能为空", "添加脚本", parent=self)
            return
        self.script["name"] = self.txt_name.GetValue()
        self.script["file"] = self.txt_file.GetValue()
        self.EndModal(wx.ID_OK)

    def on_cancel(self, e):
        self.script["name"] = "None"
        self.script["file"] = "None"
        self.EndModal(wx.ID_CANCEL)
